﻿//  Copyright © Decebal Mihailescu 2013-2014
//  All rights reserved. This source code and binaries are distributed under 
//  Microsoft Public PatternLicense (Ms-PL)
//  The latest code is available at http://VSDesignPatterns.codeplex.com

//  Redistributions of source code must retain the above copyright notice, 
//  this list of conditions and the following disclaimer. 
//  Redistributions in binary form must reproduce the above copyright notice, 
//  this list of conditions and the following disclaimer in the documentation 
//  and/or other materials provided with the distribution. 

//  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
//  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
//  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
//  PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER 
//  REMAINS UNCHANGED.
using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.ComponentModel;
using System.Reflection;
using System.IO;
using Microsoft.Win32;
using System.Diagnostics;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Xml.Linq;
using System.Xml;
using System.Globalization;
using System.Text.RegularExpressions;
using System.Net;

namespace Common
{
#if !VS2015
    using Ionic.Zip;
#endif
    using LanguageProviderEx = Lazy<Common.ILanguageProvider, Common.ILanguageProviderMetaData>;
    using SearchProviderEx = Lazy<Common.SearchProvider, Common.ISearchProviderMetaData>;

    [MetadataAttribute]
    [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
    public class ExportLanguageProviderAttribute : ExportAttribute, ILanguageProviderMetaData
    {
        public CodeLangEnum SupportedLangs { get; set; }
        public CodeLangEnum CompatibleLangs { get; set; }
        public string Description { get; set; }
        public string ProviderName { get; set; }
        public string LPIndexFile { get; set; }
        public string LPRepositoryName { get; set; }
        public string[] Extensions { get; set; }
        public PatternLicense LicenseType { get; set; }
        public StorageType SavedAs { get; set; }
        public ExportLanguageProviderAttribute()
            : base(typeof(ILanguageProvider))
        {
            LicenseType = PatternLicense.Free;
            Description = ProviderName = string.Empty;
            LPIndexFile = null;
            SavedAs = StorageType.FileSystem;
            LPRepositoryName = "TemplateLibrary";
        }

    }

    [MetadataAttribute]
    [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
    public class ExportSearchProviderAttribute : ExportAttribute, ISearchProviderMetaData
    {
        public WindowPopupEnum SupportedWnds { get; set; }
        public string Description { get; set; }
        public string ProviderName { get; set; }
        public string LPIndexFile { get; set; }
        public string URL { get; set; }
        public PatternLicense LicenseType { get; set; }
        public ExportSearchProviderAttribute()
            : base(typeof(SearchProvider))
        {
            LicenseType = PatternLicense.Free;
            Description = ProviderName = string.Empty;
        }

    }
    public abstract class BaseLanguageProvider<R, T> : IServiceProvider, IDisposable
        where R : class, IRepository, new()
        where T : class, ITemplatePattern, new()
    {

        readonly protected R _repository;
#if !VS2015
        internal ZipFile _zip;
        protected ZipFile Zip { get { return _zip; } set { _zip = value; } }
#endif
        virtual public void AboutLP(IWizardWindow iww)
        {
            ILanguageProviderMetaData ilpmd = GetService(typeof(ILanguageProviderMetaData)) as ILanguageProviderMetaData;
            if (ilpmd != null)
            {
                string path = this.GetType().Assembly.Location;
                MessageBox.Show(iww as IWin32Window,
                    string.Format("Provider Name:{0}\r\n{1}\r\nLicense:{2}\r\nCompatibleLangs:{3}\r\nSupportedLangs:{4}\r\nExtensions:{5}\r\nPath:{6}\r\nTo remove the '{0}' patterns from the grid, click on\r\nConfiguration->Language Providers and uncheck 'InUse' for it.",
                    ilpmd.ProviderName, ilpmd.Description, ilpmd.LicenseType, ilpmd.CompatibleLangs, ilpmd.SupportedLangs,
                    string.Join(";", ilpmd.Extensions), path), "Language Provider Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
        }
        public bool IsValidExtension(string ext)
        {
            if (ext == ".*")
                return true;
            string lext = ext.ToLowerInvariant();
            return this.GetAsService<ILanguageProviderMetaData>().Extensions.Any(s => s == lext);
        }

        public virtual bool IsValidArgument(ITemplateArgument ita)
        {
            if (ita == null)
                return false;
            ILanguageProviderMetaData ilpmd = ita.GetAsService<ILanguageProviderMetaData>();
            //switch (ilpmd.SupportedLangs)
            //{
            //    default:

            if (ita.Value == null)
                return false;
            string val = ita.Value.Trim();
            if (val.Length == 0 || !char.IsLetter(val[0]))
                return false;
            if (ita is TemplateArgument)
            {

                return val.ToCharArray().All(c => char.IsLetterOrDigit(c));
            }
            else
                if (ita is TemplateRuntimeArgument)
            {
                if (val[0] == '.' || val[val.Length - 1] == '.')
                    return false;
                return val.ToCharArray().All(c => char.IsLetterOrDigit(c) || c == '.');
            }
            else
                return false;
            //        break;

            //}
        }

        public virtual bool UnregisterLP(IDictionary savedState)
        {
            bool bRes = false;
            try
            {
                bRes = Utils.LangProviderRegistration.UnregisterLanguageProvider(this.GetAsService<ILanguageProviderMetaData>());
                string fileName = savedState["AddinPath"] as string;

                if (!string.IsNullOrEmpty(fileName) && File.Exists(fileName))
                {
                    Logging.Instance.Log(3, "Uninstall: deleting '{0}'", fileName);
                    File.Delete(fileName);
                }
                else
                    Logging.Instance.Log(2, "Uninstall: AddinPath stored null");
                fileName = savedState["TemplateLibZip"] as string;
                if (!string.IsNullOrEmpty(fileName) && File.Exists(fileName))
                {
                    Logging.Instance.Log(3, "Uninstall: deleting file '{0}'", fileName);
                    File.Delete(fileName);
                }
                else
                    Logging.Instance.Log(2, "Uninstall: TemplateLibzip stored null");

                ILanguageProviderMetaData ilpmd = this.GetAsService<ILanguageProviderMetaData>();
                if (ilpmd.SavedAs == StorageType.FileSystem)
                {
                    string libPath = savedState["TemplateLibPath"] as string;

                    // object storageType = (savedState["StorageType"] == null) ? null : Enum.Parse(typeof(StorageType), savedState["StorageType"] as string, true);
                    if (!string.IsNullOrEmpty(libPath) && Directory.Exists(libPath))
                    {
                        Logging.Instance.Log(3, "UnregisterLP: deleting folder '{0}'", libPath);
                        new DirectoryInfo(libPath).Delete(true);
                    }
                    else
                        Logging.Instance.Log(2, "UnregisterLP: TemplateLibPath stored '{0}' or the folder does not exist", libPath);
                }
                bRes = true;
            }
            catch (Exception ex)
            {
                bRes = false;
                Logging.Instance.Log(1, "Exception thrown in UnregisterLP " + ex.Message);
            }
            return bRes;
        }
        public virtual bool RegisterLP(bool bInUse, IDictionary savedState, string msiDirPath, IWin32Window iParentWnd)
        {

            try
            {
                bool bres = false;
                try
                {
                    ILanguageProviderMetaData ilpmd = this.GetAsService<ILanguageProviderMetaData>();
                    bres = CommonLPRegistration(bInUse, ilpmd);
                    //use System.Runtime.InteropServices.RegistrationServices.RegisterAssembly
                    //to register non free assemblies
                    //RegistrationServices rs = new RegistrationServices();
                    //if (ilpmd.LicenseType != PatternLicense.Free)
                    //    rs.RegisterAssembly(asm, AssemblyRegistrationFlags.SetCodeBase);
                    string assemblyFolder = Path.GetDirectoryName(MEFController.Instance.DPEntryAssembly.Location);
                    DirectoryInfo dirInfo = new DirectoryInfo(assemblyFolder);
                    string baseDir = dirInfo.Parent.FullName;

                    try
                    {
                        // enable it when "TemplateLibrary.zip" is not part of the msi

                        if (savedState == null)
                        {

                            return true;
                        }

                        Logging.Instance.Log(3, "msiDirPath = {0}", msiDirPath);
                        if (msiDirPath.EndsWith("\\\\"))
                            msiDirPath = msiDirPath.Remove(msiDirPath.Length - 1);//removes last '\'
                        DirectoryInfo dinf = Directory.GetParent(msiDirPath);//.Parent;
                        FileInfo fi = dinf.GetFiles("TemplateLibrary.zip", SearchOption.TopDirectoryOnly).FirstOrDefault();
                        if (fi == null)
                            fi = dinf.Parent.GetFiles("TemplateLibrary.zip", SearchOption.TopDirectoryOnly).FirstOrDefault();

                        if (fi == null)
                        {
                            Utils.ShowModalDlg("No TemplateLibrary.zip in the folder\r\n" + dinf.FullName, "Can't find TemplateLibrary.zip", MessageBoxButtons.OK, MessageBoxIcon.Error);
                            Logging.Instance.Log(1, "No TemplateLibrary.zip in the folder\r\n" + dinf.FullName);
                            throw new Exception("No TemplateLibrary.zip in " + dinf.FullName);
                        }
                        //else
                        //{

                        string libdest = Path.Combine(baseDir, "TemplateLibrary.zip");
                        Logging.Instance.Log(3, "Copying '{0}' in '{1}'", fi.FullName, libdest);
                        File.Copy(fi.FullName, libdest, true);
                        if (savedState["TemplateLibZip"] == null)
                            savedState.Add("TemplateLibZip", libdest);
                        else
                            return true;
                        string libPath = Path.Combine(baseDir, "TemplateLibrary");

                        if (savedState["TemplateLibPath"] == null)
                            savedState.Add("TemplateLibPath", libPath);
                        //must unzip default zip anyway since the storage type is not yet known, or should be moved in the registration
                        if (ilpmd.SavedAs == StorageType.FileSystem)
                        {
                            //clear the old template dir first
                            if (Directory.Exists(libPath))
                            {
                                DirectoryInfo di = new DirectoryInfo(libPath);
                                di.RemoveReadOnlyAttribute(System.IO.SearchOption.AllDirectories);
                                di.Delete(true);
                            }
                            Logging.Instance.Log(3, "RegisterLP: unzipping {0} in {1}", libdest, baseDir);
#if !VS2015
                            Utils.UnzipLibrary(libdest, baseDir);
#endif
                        }
                        //else
                        //    Logging.Instance.Log(3,"Install: skip unzipping {0} in {1}, storage type = {2}", destFolder, zipPath, WizardUtils.defaultStorage);
#if !VS2015
                        RegistryKey reg = Registry.CurrentUser.CreateSubKey(Common.Utils.AppHive, RegistryKeyPermissionCheck.ReadWriteSubTree);
                        if (reg != null)
                        {
                            reg.SetValue("TemplateLibPath", libPath, RegistryValueKind.String);
                            reg.Flush();
                            reg.Close();
                            Logging.Instance.Log(3, "set registry TemplateLibPath to {0} ", libPath);
                        }
                        else
                            Logging.Instance.Log(1, "Install: can't create registry key {0}", Common.Utils.AppHive);
#endif

                    }
                    catch (System.Exception ex)
                    {
                        Logging.Instance.Log(1, "Install: exception thrown wile unziping and TemplateLibPath registry set: {0}", ex.Message);
                        throw new Exception("Can't set TemplateLibPath", ex);
                    }
                }
                catch (Exception ex)
                {

                    Logging.Instance.Log(1, "Exception thrown in RegisterLP: {0} ", ex.Message);
                    bres = false;
                }
                return bres;

            }
            catch (Exception ex)
            {

                Logging.Instance.Log(1, "Exception thrown in RegisterLP: {0} ", ex.Message);
            }

            return true;
        }

        protected bool CommonLPRegistration(bool bInUse, ILanguageProviderMetaData ilpmd)
        {
            bool bres = false;
            using (RegistryKey regPlugIns = Registry.CurrentUser.CreateSubKey(Utils.PlugInsHive, RegistryKeyPermissionCheck.ReadWriteSubTree))
            {
                string regpath = ilpmd.ProviderName + "\\VS" + Utils.VSYear;
                RegistryKey regPlugIn = regPlugIns.OpenSubKey(regpath, RegistryKeyPermissionCheck.ReadWriteSubTree);
                if (regPlugIn == null)
                {
                    regPlugIn = regPlugIns.CreateSubKey(regpath, RegistryKeyPermissionCheck.ReadWriteSubTree);
                    regPlugIn.SetValue("InUse", 1, RegistryValueKind.DWord);

                    Logging.Instance.Log(3, "set registry registry plug in for {0} ", regpath);
                    bres = true;
                }
                else
                {
                    regPlugIn.SetValue("InUse", bInUse ? 1 : 0, RegistryValueKind.DWord);
                    Logging.Instance.Log(3, "registry key {0} exists", regpath);
                }

                regPlugIn.SetValue("Location", this.GetType().Assembly.Location, RegistryValueKind.String);
                regPlugIn.Flush();
                regPlugIn.Close();
            }
            return bres;
        }

        protected string Logo { get; set; }
        public IEnumerable<FileData> GenerateCodeFiles(ITemplatePattern ipt)
        {
            if (ipt == null || ipt.TemplateFiles == null)
                return new FileData[] { };
            return ipt.TemplateFiles.Select(t =>
            {
                if (t == null || string.IsNullOrEmpty(t.FileTemplateContent))
                    return null;
                string tmpPath = Path.GetTempFileName();
                File.AppendAllText(tmpPath, Logo + System.Environment.NewLine + GetPatternInfo(t, ipt) + Utils.ExecuteArgSubstitution(t, ipt.Arguments), System.Text.Encoding.UTF8);
                return new FileData() { fileNameCopy = tmpPath, itemName = t.FileItemName };

            });
        }
        virtual protected string LineComment
        {
            get { return @"//"; }
        }
        private string GetPatternInfo(ITemplateFile itf, ITemplatePattern ipt)
        {
            if (itf == null || ipt == null || !Utils.AddPatternDescrition)
                return null;
            IWizardWindow iww = this.GetAsService<IWizardWindow>();
            if (iww == null || iww.WBDescription.Document == null)//shold check for Completed event too and !iww.WBDescription.Visible ||
                return null;
            string path = ipt.Description.ToLower();
            if (!path.Contains(ipt.PatternName.ToLower()))
                return null;
            switch (Path.GetExtension(path))
            {
                case ".htm":
                case ".html":
                    break;
                default:
                    return null;

            }

            try
            {
                HtmlElement el = iww.WBDescription.Document.Body.GetElementsByTagName("h2").OfType<HtmlElement>().FirstOrDefault(e => e.InnerText.Trim() == "Intent");
                if (el == null)
                {//class="ui-widget-header"
                    var lst = iww.WBDescription.Document.Body.GetElementsByTagName("div").Cast<HtmlElement>();
                    const string match1 = Utils.Collapse + "Intent";
                    const string match2 = Utils.Expand + "Intent";
                    HtmlElement elem = lst.FirstOrDefault(e => e.InnerText.Trim() == match1 || e.InnerText.Trim() == match2);//.ToArray();//.Where(e=>e.GetAttribute("className") == "ui-widget-header");//.FirstOrDefault(e => e.InnerText.Trim() == "Intent"); ;
                    if (elem == null || elem.NextSibling == null)
                        return null;
                    el = elem.NextSibling;
                    el = elem.NextSibling.GetElementsByTagName("p").OfType<HtmlElement>().FirstOrDefault();
                    if (el == null)
                        return null;
                }
                else
                {
                    HtmlElement pr = el.Parent;
                    el = pr.Children.OfType<HtmlElement>().FirstOrDefault(e => string.Compare(e.TagName, "P", true) == 0);
                    if (el == null)
                        return null;
                }
                string result = Regex.Replace(el.InnerHtml, @"<\s*[Aa][^>]*>[^<]*<[^>][Aa]\s*>", String.Empty, RegexOptions.IgnoreCase);//remove all A tags

#if !VS2008
                result = System.Net.WebUtility.HtmlDecode(result);
#endif
                result = Regex.Replace(result, @"<\s*br[^>]*>", "\n", RegexOptions.IgnoreCase);

                result = result.Replace("\r\n", "\n").Trim();
                var lines = result.Split(new string[] { "\n" }, StringSplitOptions.RemoveEmptyEntries).Select(s => LineComment + s.Trim());
#if VS2008
                result = string.Join("\r\n", lines.ToArray());
#else
                result = string.Join("\r\n", lines);
#endif

                if (result.Contains(ipt.PatternName))
                    result = string.Format("{2}{0}{1}{2}", LineComment, result, Environment.NewLine);
                else
                    result = string.Format("{3}{0}{1}: {2}{3}", LineComment, ipt.PatternName, result, Environment.NewLine);
                return result;
            }
            catch
            {
                return null;
            }
        }
        public string PatternsVersion { get; protected set; }
        protected string PatternDefinitions { get; set; }
        static internal readonly bool _Is64bitProc;

        static BaseLanguageProvider()
        {

            try
            {
                _Is64bitProc = System.Runtime.InteropServices.Marshal.SizeOf(typeof(IntPtr)) == 8;
            }
            catch (Exception)
            {

                throw;
            }
        }

        readonly protected StorageType Storage;
        public BaseLanguageProvider()
        {
            try
            {
                //! the subclass might not be build at this time and GetService might throw
                _repository = new R();
                _repository.Init(this as ILanguageProvider);
            }
            catch
            {
            }

        }
        protected virtual bool CanLoadLang(string lng)
        {
            try
            {
                CodeLangEnum le = ((CodeLangEnum)Enum.Parse(typeof(CodeLangEnum), lng, true));
                ILanguageProviderMetaData ilpmd = this.GetAsService<ILanguageProviderMetaData>();
                return (le & ilpmd.SupportedLangs) != default(CodeLangEnum);//
                //return string.Compare(lng, MEFController.ProjectInfo.CodeLang.ToString(), true) == 0;
            }
            catch (Exception ex)
            {
                Logging.Instance.Log(1, "BaseLanguageProvider.CanLoadLang exception: " + ex.Message);
                return false;
                // throw;
            }
        }

        public virtual IEnumerable<ITemplatePattern> LoadTemplates(IWin32Window iwnd)
        {

            if (MEFController.ProjectInfo == null)
                return null;
            XmlReader xmlReader = null;
            try
            {
                xmlReader = _repository.GetIndex(iwnd);
                if (xmlReader == null)
                {
#if DEBUG
                    iwnd.ShowMessageBox("Can't get index from " + (_repository as Repository).TemplatesFolder, "Can not load templates");
#endif
                    return null;
                }
                XDocument doc = XDocument.Load(xmlReader, LoadOptions.None);
                PatternsVersion = (doc.Root.Attribute("PatternsVersion") == null) ? null : doc.Root.Attribute("PatternsVersion").Value;
                //selects only relevant nodes based on language
                IEnumerable<XElement> templs = doc.Root.Elements("PatternTemplate").Where(x => CanLoadLang(x.Attribute("CodeLang").Value));
                return templs.Select(e =>
                {
                    ITemplatePattern t = new T();
                    t.Init(this as ILanguageProvider, e);
                    return t;
                });
            }
            catch (Exception ex)
            {
                iwnd.ShowMessageBox(ex.Message, "Can not load templates");

                return null;
            }
            finally
            {
                if (xmlReader != null)
                    xmlReader.Close();
            }
        }

        #region IServiceProvider Members

        public virtual object GetService(Type serviceType)
        {
            if (serviceType == typeof(IRepository))
                return _repository;
            if (serviceType == typeof(ILanguageProvider) || serviceType == typeof(IServiceProvider))
                return this;
            if (serviceType == typeof(IWizardWindow) || serviceType == typeof(ITabWizard))
            {
                if (MEFController.ProjectInfo == null || MEFController.ProjectInfo.GetAsService<IWizardWindow>() == null)
                    return null;
                else
                    return MEFController.ProjectInfo.GetAsService<IWizardWindow>().GetService(serviceType);
            }
            if (serviceType == typeof(IProjectInfo))// || serviceType == typeof(ProjectInfo))
                return MEFController.ProjectInfo;
            object res = (MEFController.ProjectInfo == null) ? null : MEFController.ProjectInfo.GetService(serviceType);
            if (res != null)
                return res;
            return res;
        }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
#if !VS2015
            if (Zip != null)
            {
                Zip.Dispose();
                Zip = null;
                GC.SuppressFinalize(this);
            }
#endif
        }
        ~BaseLanguageProvider()
        {
            Dispose();
        }
        #endregion
    }
    public interface ITemplateFile : IServiceProvider
    {
        string FileItemName { get; }
        string FileTemplate { get; }
        StorageType StoredAs { get; }
        string FileTemplateContent { get; set; }

    }
    public interface IRepository : IDisposable
    {
        void Init(ILanguageProvider ilpmd);
        string GetCodeFilePath(Common.ITemplateFile itf);
        void GetDescription(Common.ITemplatePattern itp, System.Xml.Linq.XElement el);
        XmlReader GetIndex(System.Windows.Forms.IWin32Window iwnd);
        void GetPicture(Common.ITemplatePattern itp, System.Xml.Linq.XElement el);
        string GetTemplatePath(string PatternName);
        void LoadTFContent(ITemplateFile itf);
        void UpdatePicture(ITemplatePattern itp, IWizardWindow iww);
        void UpdateDescription(ITemplatePattern itp, IWizardWindow iww);
        Stream this[string newlocation] { get; }
    }
    public interface ITemplatePattern : IServiceProvider
    {
        void Init(ILanguageProvider languageProvider, XElement templateElem);
        string PatternName { get; set; }
        string PatternAlias { get; set; }
        string Description { get; set; }
        StorageType DescriptionStoredAs { get; set; }
        float MinVSVersion { get; set; }// will filter features like optional params, async/await,dynamic
        string Category { get; set; }//behavioral,creational
        void UIFormatTemplatePattern(DataGridViewCellFormattingEventArgs e);
        bool CanGeneratePattern(IWizardWindow iww);
        string CompilerSyntax { get; set; }//managed,native,VC10, empty
        string ProgrammingStyle { get; set; }//generic,template,plain,nothing
        string Culture { get; set; }
        // bool IsDefault { get; }
        IEnumerable<ITemplateArgument> Arguments { get; set; }
        CodeLangEnum CodeLanguage { get; set; }//string version of CodeLanguageEnum
        string Picture { get; set; }
        StorageType PictureStoredAs { get; set; }
        string OnlineResources { get; set; }
        IEnumerable<ITemplateFile> TemplateFiles { get; set; }
        int TemplateVersion { get; }
    }

    public interface ITemplateArgument : IServiceProvider
    {
        string ArgumentName { get; }
        string Description { get; }
        string Value { get; set; }
        bool IsRuntime { get; }
        //  bool IsValid { get; }
    }


    public class EventWizardWindowClosed : EventArgs
    {
        public int CloseReason;
    }

    public delegate void WizardWindowClosedDelegate(object sender, EventWizardWindowClosed e);
    public interface IAboutWnd
    {
        DialogResult ShowDialog(IWin32Window owner);
    }
    public interface IWizardWindow : IDisposable, IServiceProvider
    {
        DialogResult ShowWizardWindow(IWin32Window parent);
        IWin32Window VSWindow { get; }
        PictureBox Diagram { get; }
        void LoadImageFromStream(Stream strp);
        RichTextBox RTBDescription { get; }
        WebBrowser WBDescription { get; }
        LinkLabel OnlineResources { get; }
        event WizardWindowClosedDelegate WizardWindowClosed;
        bool IsRestarting { get; set; }
    }
    public interface ITabWizard : IServiceProvider
    {
        IEnumerable<ITemplatePattern> Templates { get; }
        ITemplatePattern SelectedPattern { get; }
    }
    public interface ILanguageProvider : IServiceProvider
    {
        bool IsValidExtension(string ext);
        IEnumerable<FileData> GenerateCodeFiles(ITemplatePattern ipt);
        IEnumerable<ITemplatePattern> LoadTemplates(IWin32Window iwnd);
        string PatternsVersion { get; }
        bool RegisterLP(bool bLoad, IDictionary savedState, string msiDirPath, IWin32Window iParentWnd);
        bool UnregisterLP(IDictionary savedState);
        void AboutLP(IWizardWindow iww);
        bool IsValidArgument(ITemplateArgument ita);
    }
#if VS2015
    public interface IPackageInstall
    {
        void PostInstallPackage(IWin32Window iwnd = null);
    }
#endif

    public static class Utils
    {
        public const string Expand = "[+]"; 
        public const string Collapse = "[-]"; 
        public static string GetDescription(this Enum value)
        {
            DescriptionAttribute attribute = value.GetType()
                .GetField(value.ToString())
                .GetCustomAttributes(typeof(DescriptionAttribute), false)
                .SingleOrDefault() as DescriptionAttribute;
            return attribute == null ? value.ToString() : attribute.Description;
        }

        public static T GetEnumValueFromDescription<T>(string description)
        {
            var type = typeof(T);
            if (!type.IsEnum)
                throw new ArgumentException();
            FieldInfo[] fields = type.GetFields();
            var field = fields
                            .SelectMany(f => f.GetCustomAttributes(
                                typeof(DescriptionAttribute), false), (
                                    f, a) => new { Field = f, Att = a })
                            .Where(a => ((DescriptionAttribute)a.Att)
                                .Description == description).SingleOrDefault();
            return field == null ? default(T) : (T)field.Field.GetRawConstantValue();
        }

#if VS2008
        /// <summary>     
        /// The function determines whether the current operating system is a      
        /// 64-bit operating system.     
        /// </summary>     
        /// <returns>     
        /// The function returns true if the operating system is 64-bit;      
        /// otherwise, it returns false.     
        /// </returns>    
        public static bool IsWin64BitOS(this OperatingSystem os)
        {
            if (IntPtr.Size == 8)
                // 64-bit programs run only on Win64           
                return true;
            else// 32-bit programs run on both 32-bit and 64-bit Windows     
            {   // Detect whether the current process is a 32-bit process                
                // running on a 64-bit system.               
                return Process.GetCurrentProcess().Is64BitProc();
            }
        }

        /// <summary>  
        /// Checks if the process is 64 bit  
        /// </summary>  
        /// <param name="os"></param>  
        /// <returns>  
        /// The function returns true if the process is 64-bit;        
        /// otherwise, it returns false.  
        /// </returns>    
        public static bool Is64BitProc(this System.Diagnostics.Process p)
        {
            // 32-bit programs run on both 32-bit and 64-bit Windows           
            // Detect whether the current process is a 32-bit process                
            // running on a 64-bit system.               
            bool result;
            return ((DoesWin32MethodExist("kernel32.dll", "IsWow64Process") && IsWow64Process(p.Handle, out result)) && result);
        }

        /// <summary>     
        /// The function determins whether a method exists in the export      
        /// table of a certain module.     
        /// </summary>     
        /// <param name="moduleName">The name of the module</param>     
        /// <param name="methodName">The name of the method</param>     
        /// <returns>     
        /// The function returns true if the method specified by methodName      
        /// exists in the export table of the module specified by moduleName.     
        /// </returns>       
        static bool DoesWin32MethodExist(string moduleName, string methodName)
        {
            IntPtr moduleHandle = GetModuleHandle(moduleName);
            if (moduleHandle == IntPtr.Zero)
                return false;
            return (GetProcAddress(moduleHandle, methodName) != IntPtr.Zero);
        }
        [DllImport("kernel32.dll")]
        static extern IntPtr GetCurrentProcess();

        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        static extern IntPtr GetModuleHandle(string moduleName);

        [DllImport("kernel32", CharSet = CharSet.Auto, SetLastError = true)]
        static extern IntPtr GetProcAddress(IntPtr hModule, [MarshalAs(UnmanagedType.LPStr)]string procName);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool IsWow64Process(IntPtr hProcess, out bool wow64Process);
#endif
        public static string VSPath
        {
            get
            {
                string vsPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles),
                        string.Format(@"Microsoft Visual Studio {0}\Common7\IDE\DEVENV.exe", Utils.VSVersion));
#if VS2008
                bool bIs64BitOS = System.Environment.OSVersion.IsWin64BitOS();
#else
                bool bIs64BitOS = Environment.Is64BitOperatingSystem;
#endif
                try
                {//use the registry to get the prex from 
                    //HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\10.0\Setup\VS@EnvironmentPath
                    string regpath = string.Format(bIs64BitOS ? @"SOFTWARE\Wow6432Node\Microsoft\VisualStudio\{0}\Setup\VS" : @"SOFTWARE\Microsoft\VisualStudio\{0}\Setup\VS", Utils.VSVersion);
                    using (RegistryKey reg = Registry.LocalMachine.OpenSubKey(regpath, false))
                    {
                        if (reg != null)
                        {
                            vsPath = reg.GetValue("EnvironmentPath", vsPath, RegistryValueOptions.None) as string;
                        }
                        else
                            Logging.Instance.Log(2, "EnvironmentPath does not exist for VS version {0}", Utils.VSVersion);
                        return vsPath;
                    }
                }
                catch (Exception ex)
                {
                    vsPath = (bIs64BitOS ? @"c:\Program Files (x86)\Microsoft Visual Studio " : @"c:\Program Files\Microsoft Visual Studio ")
                        + Utils.VSVersion + @"\Common7\IDE\devenv.exe";
                    Logging.Instance.Log(1, "Error while getting VS prex :{0}, falling back to '{1}", ex.Message, vsPath);
                    return vsPath;
                }
            }
        }
        public static T GetAssemblyAttribute<T>(this System.Reflection.Assembly ass)
            where T : Attribute
        {
            object[] attributes = ass.GetCustomAttributes(typeof(T), false);
            if (attributes == null || attributes.Length == 0)
                return null;
            return attributes.OfType<T>().SingleOrDefault();
        }
        static public DateTime LastOnlineCheck
        {
            get
            {
                RegistryKey reg = null;
                try
                {
                    reg = Registry.CurrentUser.OpenSubKey(AppHive, false);
                    if (reg != null)
                    {

                        long ticks = (long)reg.GetValue("LastOnlineCheck", 0L);
                        return (ticks == 0L) ? DateTime.Today : new DateTime(ticks);
                    }
                    else
                    {
                        return DateTime.Today;
                    }
                }
                catch (Exception)
                {

                    return DateTime.Today;
                }
                finally
                {
                    if (reg != null)
                        reg.Close();
                }
            }
            set
            {
                using (RegistryKey reg = Registry.CurrentUser.CreateSubKey(AppHive, RegistryKeyPermissionCheck.ReadWriteSubTree))
                {
                    reg.SetValue("LastOnlineCheck", (ulong)value.Ticks, RegistryValueKind.QWord);
                }
            }
        }
        static public string LastOnlineVer
        {
            get
            {
                RegistryKey reg = null;
                try
                {
                    reg = Registry.CurrentUser.OpenSubKey(AppHive, false);
                    if (reg != null)
                    {
                        return reg.GetValue("LastOnlineVersion", "9.9") as string;
                    }
                    else
                    {
                        return "9.9";
                    }
                }
                catch (Exception)
                {

                    return "9.9";
                }
                finally
                {
                    if (reg != null)
                        reg.Close();
                }
            }
            set
            {
                using (RegistryKey reg = Registry.CurrentUser.CreateSubKey(AppHive, RegistryKeyPermissionCheck.ReadWriteSubTree))
                {
                    reg.SetValue("LastOnlineVersion", value, RegistryValueKind.String);
                }
            }
        }
        static public int OnlineResourcesDestination
        {
            get
            {
                RegistryKey reg = null;
                try
                {
                    reg = Registry.CurrentUser.OpenSubKey(AppHive, false);
                    if (reg != null)
                    {
                        return (int)reg.GetValue("OnlineResourcesDestination", 2);
                    }
                    else
                    {
                        return 2;
                    }
                }
                catch (Exception)
                {

                    return 2;
                }
                finally
                {
                    if (reg != null)
                        reg.Close();
                }
            }
            set
            {
                using (RegistryKey reg = Registry.CurrentUser.CreateSubKey(AppHive, RegistryKeyPermissionCheck.ReadWriteSubTree))
                {
                    reg.SetValue("OnlineResourcesDestination", value, RegistryValueKind.DWord);
                }
            }
        }
        static public int OnlineSearchDestination
        {
            get
            {
                RegistryKey reg = null;
                try
                {
                    reg = Registry.CurrentUser.OpenSubKey(AppHive, false);
                    if (reg != null)
                    {
                        return (int)reg.GetValue("OnlineSearchDestination", 2);
                    }
                    else
                    {
                        return 2;
                    }
                }
                catch (Exception)
                {

                    return 2;
                }
                finally
                {
                    if (reg != null)
                        reg.Close();
                }
            }
            set
            {
                using (RegistryKey reg = Registry.CurrentUser.CreateSubKey(AppHive, RegistryKeyPermissionCheck.ReadWriteSubTree))
                {
                    reg.SetValue("OnlineSearchDestination", value, RegistryValueKind.DWord);
                }
            }
        }
        static public bool ShowSearchMenus
        {
            get
            {
                RegistryKey reg = null;
                try
                {
                    reg = Registry.CurrentUser.OpenSubKey(AppHive, false);
                    if (reg != null)
                    {
                        return (int)reg.GetValue("ShowSearchMenus", 1) != 0;
                    }
                    else
                    {
                        return true;
                    }
                }
                catch (Exception)
                {

                    return true;
                }
                finally
                {
                    if (reg != null)
                        reg.Close();
                }
            }
            set
            {
                using (RegistryKey reg = Registry.CurrentUser.CreateSubKey(AppHive, RegistryKeyPermissionCheck.ReadWriteSubTree))
                {
                    reg.SetValue("ShowSearchMenus", value ? 1 : 0, RegistryValueKind.DWord);
                }
            }
        }
        static public bool AddPatternDescrition
        {
            get
            {
                RegistryKey reg = null;
                try
                {
                    reg = Registry.CurrentUser.OpenSubKey(AppHive, false);
                    if (reg != null)
                    {
                        return (int)reg.GetValue("AddPatternDescrition", 1) != 0;
                    }
                    else
                    {
                        return true;
                    }
                }
                catch (Exception)
                {

                    return true;
                }
                finally
                {
                    if (reg != null)
                        reg.Close();
                }
            }
            set
            {
                using (RegistryKey reg = Registry.CurrentUser.CreateSubKey(AppHive, RegistryKeyPermissionCheck.ReadWriteSubTree))
                {
                    reg.SetValue("AddPatternDescrition", value ? 1 : 0, RegistryValueKind.DWord);
                }
            }
        }
        static public bool CollapseHeaders
        {
            get
            {
                RegistryKey reg = null;
                try
                {
                    reg = Registry.CurrentUser.OpenSubKey(AppHive, false);
                    if (reg != null)
                    {
                        return (int)reg.GetValue("CollapseHeaders", 1) != 0;
                    }
                    else
                    {
                        return true;
                    }
                }
                catch (Exception)
                {

                    return true;
                }
                finally
                {
                    if (reg != null)
                        reg.Close();
                }
            }
            set
            {
                using (RegistryKey reg = Registry.CurrentUser.CreateSubKey(AppHive, RegistryKeyPermissionCheck.ReadWriteSubTree))
                {
                    reg.SetValue("CollapseHeaders", value ? 1 : 0, RegistryValueKind.DWord);
                }
            }
        }
        static public bool StartExpanded
        {
            get
            {
                RegistryKey reg = null;
                try
                {
                    reg = Registry.CurrentUser.OpenSubKey(AppHive, false);
                    if (reg != null)
                    {
                        return (int)reg.GetValue("StartExpanded", 1) != 0;
                    }
                    else
                    {
                        return true;
                    }
                }
                catch (Exception)
                {

                    return true;
                }
                finally
                {
                    if (reg != null)
                        reg.Close();
                }
            }
            set
            {
                using (RegistryKey reg = Registry.CurrentUser.CreateSubKey(AppHive, RegistryKeyPermissionCheck.ReadWriteSubTree))
                {
                    reg.SetValue("StartExpanded", value ? 1 : 0, RegistryValueKind.DWord);
                }
            }
        }
        static public bool SortPatternsOnLoad
        {
            get
            {
                RegistryKey reg = null;
                try
                {
                    reg = Registry.CurrentUser.OpenSubKey(AppHive, false);
                    if (reg != null)
                    {
                        return (int)reg.GetValue("SortPatternsOnLoad", 0) != 0;
                    }
                    else
                    {
                        return false;
                    }
                }
                catch (Exception)
                {

                    return false;
                }
                finally
                {
                    if (reg != null)
                        reg.Close();
                }
            }
            set
            {
                using (RegistryKey reg = Registry.CurrentUser.CreateSubKey(AppHive, RegistryKeyPermissionCheck.ReadWriteSubTree))
                {
                    reg.SetValue("SortPatternsOnLoad", value ? 1 : 0, RegistryValueKind.DWord);
                }
            }
        }
        static public bool HideOnlineLink
        {
            get
            { //default is hide (i as value)
                RegistryKey reg = null;
                try
                {
                    reg = Registry.CurrentUser.OpenSubKey(AppHive, false);
                    if (reg != null)
                    {
                        return (int)reg.GetValue("HideOnlineLink", 1) != 0;
                    }
                    else
                    {
                        return true;
                    }
                }
                catch (Exception)
                {

                    return true;
                }
                finally
                {
                    if (reg != null)
                        reg.Close();
                }
            }
            set
            {
                using (RegistryKey reg = Registry.CurrentUser.CreateSubKey(AppHive, RegistryKeyPermissionCheck.ReadWriteSubTree))
                {
                    reg.SetValue("HideOnlineLink", value ? 1 : 0, RegistryValueKind.DWord);
                }
            }
        }
        public const string BaseURL = "http://vsdesignpatterns.codeplex.com/";
        public const string AppHive = @"Software\DesignPatterns\VS" + Utils.VSYear;


        public static bool NewOnlineVersionExists(out string downloadUrl, out float onlineVersion)
        {

            bool isnew = false;
            downloadUrl = null;
            onlineVersion = 0;

            downloadUrl = BaseURL;
            try
            {

                using (WebClient client = new WebClient())
                {
                    string result = client.DownloadString(downloadUrl);
                    string version = result.FindFirstMatchingGroup(@"<td>\s*VS[ ]*Design[ ]*Patterns[ ]*(?<Version>[0-9,.]+)\s*</td>", "Version", RegexOptions.IgnoreCase).Trim();
                    Logging.Instance.Log(3, "Online version:" + version);
                    if (!string.IsNullOrEmpty(version))
                    {

                        LastOnlineVer = version;
                        if (!float.TryParse(version, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture.NumberFormat, out onlineVersion))
                        {
                            Logging.Instance.Log(3, "Can't parse version:" + version);
                            return false;
                        }
                        //onlineVersion = float.Parse(version, System.Globalization.CultureInfo.InvariantCulture.NumberFormat);
                        isnew = IsNewerThanCurrent(onlineVersion);
                        if (isnew)
                        {
                            Logging.Instance.Log(3, version + " IsNewerThanCurrent");
                            string latestUrl = result.FindFirstMatchingGroup("href=\\\"(?<URL>" + BaseURL + "releases/view/[0-9]+)\\\"", "URL", RegexOptions.Multiline);
                            if (string.IsNullOrEmpty(latestUrl))
                            {
                                Logging.Instance.Log(1, "Can't find first download url");
                                return true;
                            }
                            else
                                downloadUrl = latestUrl;
                            Logging.Instance.Log(1, "first download url " + downloadUrl);
                            result = client.DownloadString(downloadUrl);
                            //get specific download link
                            latestUrl = result.FindFirstMatchingGroup("href=\\\"(?<URL>" + BaseURL + "downloads/get/[0-9]+)\\\"" + ".+>DesignPatterns" + Utils.VSYear + "_" + version + ".zip\\s*</a>", "URL", RegexOptions.Multiline);
                            if (latestUrl != null && latestUrl.Length > 20)//pass the smell test
                            {
                                downloadUrl = latestUrl;//best url to download
                                Logging.Instance.Log(1, "Best download url " + downloadUrl);
                                return true;
                            }
                            //get download link for all setups if can't find specific version
                            latestUrl = result.FindFirstMatchingGroup("href=\\\"(?<URL>" + BaseURL + "downloads/get/[0-9]+)\\\"" + ".+>DesignPatternsAllSetups_" + version + ".zip\\s*</a>", "URL", RegexOptions.Multiline);
                            if (string.IsNullOrEmpty(latestUrl))//no urll for download all, get the first download link on the page
                                latestUrl = result.FindFirstMatchingGroup("href=\\\"(?<URL>" + BaseURL + "downloads/get/[0-9]+)\\\"", "URL", RegexOptions.Multiline);
                            if (string.IsNullOrEmpty(latestUrl) || latestUrl.Length <= 20)//can't pass the smell test
                            {
                                return true;//keep the last valid url
                            }
                            downloadUrl = latestUrl;
                            Logging.Instance.Log(1, "Best download url for all:" + downloadUrl);
                        }
                    }

                }
            }
            catch (Exception ex)
            {
                Logging.Instance.Log(1, "Exception in IsnewVersion:" + ex.Message);
                /*
                if (Application.OpenForms != null && Application.OpenForms.Count > 0)
                {
                    Form frm = Application.OpenForms[0];//???
                    frm.ShowMessageBox(ex.Message, "Exception in IsnewVersion");
                }
                  */

            }
            return isnew;
        }

        public static bool IsNewerThanCurrent(float onlineVersion)
        {
            Assembly crt = MEFController.Instance.DPEntryAssembly;

            Version v = crt.GetName().Version;
            float crtVersion = float.Parse(string.Format("{0}.{1}", v.Major, v.Minor), System.Globalization.CultureInfo.InvariantCulture.NumberFormat);
            return crtVersion < onlineVersion;
        }
        internal static string FindFirstMatchingGroup(this string input, string regex, string group, RegexOptions opt)
        {
            string result = null;
            try
            {
                Match m = Regex.Match(input, regex, opt);
                if (m.Success && m.Groups[group].Success)
                {
                    result = m.Groups[group].Value;
                }
            }
            catch
            {

                result = null;
            }
            return result;
        }
#if DEBUG
        public const StorageType defaultStorage = StorageType.FileSystem;
#else
       public const StorageType defaultStorage = StorageType.Zip;
#endif
        /// <summary>
        /// Copies the contents of input to output. Doesn't close either stream.
        /// </summary>
        public static void CopyMemStream2FileStream(Stream input, Stream output)
        {
            byte[] buffer = new byte[8 * 1024];
            int len;
            while ((len = input.Read(buffer, 0, buffer.Length)) > 0)
            {
                output.Write(buffer, 0, len);
            }
        }
        static public DialogResult ShowMessageBox(this IWin32Window iwnd, string message, string title)
        {
            Control frm = iwnd as Control;
            if (frm != null && frm.InvokeRequired)
            {
                return (DialogResult)frm.Invoke(new Func<DialogResult>(() => MessageBox.Show(frm, message, title)));
            }
            else
                return (frm == null) ? MessageBox.Show(message, title) : MessageBox.Show(frm, message, title);
        }
        static public DialogResult ShowMessageBox(this IWin32Window iwnd, string message, string title, MessageBoxButtons btn)
        {
            Control frm = iwnd as Control;
            if (frm != null && frm.InvokeRequired)
            {
                return (DialogResult)frm.Invoke(new Func<DialogResult>(() => MessageBox.Show(frm, message, title, btn)));
            }
            else
                return (frm == null) ? MessageBox.Show(message, title, btn) : MessageBox.Show(frm, message, title, btn);
        }
        static public DialogResult ShowMessageBox(this IWin32Window iwnd, string message, string title, MessageBoxButtons btn, MessageBoxIcon icn)
        {
            Control frm = iwnd as Control;
            if (frm != null && frm.InvokeRequired)
            {
                return (DialogResult)frm.Invoke(new Func<DialogResult>(() => MessageBox.Show(frm, message, title, btn, icn)));
            }
            else
                return (frm == null) ? MessageBox.Show(message, title, btn, icn) : MessageBox.Show(frm, message, title, btn, icn);
        }
        static public IEnumerable<ILanguageProviderMetaData> GetMetaDataProviders(Predicate<ILanguageProviderMetaData> mdFilter)
        {
            if (mdFilter == null)
                return MEFController.Instance.LoadedLangProviders.Select(x => x.Metadata);
            else
                return MEFController.Instance.LoadedLangProviders.Where(p => p != null && mdFilter(p.Metadata)).Select(x => x.Metadata);
        }
        static public TEnum Attr2Enum<TEnum>(this System.Xml.Linq.XElement e, string attrName, TEnum defaultVal) where TEnum : struct, IComparable, IFormattable, IConvertible
        {
            if (!typeof(TEnum).IsEnum)
                throw new InvalidOperationException("Can't convert text to non Enum");
            System.Xml.Linq.XAttribute attr = e.Attribute(attrName);
            object val = (attr == null) ? defaultVal : Enum.Parse(typeof(TEnum), attr.Value, true);
            return (TEnum)val;// Enum.ToObject(typeof(TEnum), val);
        }

        public static TEnum[] GetAllEnumValues<TEnum>() where TEnum : struct, IComparable, IFormattable, IConvertible
        {
            if (typeof(TEnum).BaseType != typeof(Enum))
            {
                throw new ArgumentException(string.Format("{0} is not of type System.Enum", typeof(TEnum)));
            }
            return Enum.GetValues(typeof(TEnum)) as TEnum[];
        }

        static public IEnumerable<TEnum> GetSpecificAllFlags<TEnum>(this TEnum enumVal) where TEnum : struct, IComparable, IFormattable, IConvertible
        {

            if (!typeof(TEnum).IsEnum)
                throw new InvalidOperationException("Can't get flags from non Enum");
            IEnumerable<TEnum> sequence = null;
            object val = enumVal;
            switch (Type.GetTypeCode(Enum.GetUnderlyingType(typeof(TEnum))))
            {
                case TypeCode.Byte:
                case TypeCode.SByte:
                    sequence = Enum.GetValues(typeof(TEnum))
                                .Cast<Byte>()
                                .Where(v => (v & (byte)val) != (byte)0).Cast<TEnum>();
                    break;
                case TypeCode.Int16:
                case TypeCode.UInt16:
                    sequence = Enum.GetValues(typeof(TEnum))
                                .Cast<UInt16>()
                                .Where(v => (v & (UInt16)val) != (UInt16)0).Cast<TEnum>();
                    break;
                case TypeCode.Int32:
                case TypeCode.UInt32:
                    sequence = Enum.GetValues(typeof(TEnum))
                                .Cast<UInt32>()
                                .Where(v => (v & (UInt32)val) != (UInt32)0).Cast<TEnum>();
                    break;
                case TypeCode.Int64:
                case TypeCode.UInt64:
                    sequence = Enum.GetValues(typeof(TEnum))
                                .Cast<UInt64>()
                                .Where(v => (v & (UInt64)val) != (UInt64)0).Cast<TEnum>();
                    break;
                default:
                    throw new InvalidOperationException("unhandled enum underlying type");

            }

            return sequence;// (TEnum)Enum.ToObject(typeof(TEnum), array);
        }
        public class LangProviderRegistration : ILangProviderRegistration
        {
            private LangProviderRegistration() { }

            internal static void UpdateLPRegistration(string codeLangProviderName, bool bInUse)
            {
                try
                {
                    using (RegistryKey regPlugIns = Registry.CurrentUser.CreateSubKey(Utils.PlugInsHive, RegistryKeyPermissionCheck.ReadWriteSubTree))
                    {
                        string regpath = codeLangProviderName + "\\VS" + Utils.VSYear;
                        RegistryKey regPlugIn = regPlugIns.OpenSubKey(regpath, RegistryKeyPermissionCheck.ReadWriteSubTree);
                        if (regPlugIn == null)
                        {
                            regPlugIn = regPlugIns.CreateSubKey(regpath, RegistryKeyPermissionCheck.ReadWriteSubTree);
                            regPlugIn.SetValue("InUse", 1, RegistryValueKind.DWord);

                            Logging.Instance.Log(3, "set registry registry plug in for {0} ", regpath);
                        }
                        else
                        {
                            regPlugIn.SetValue("InUse", bInUse ? 1 : 0, RegistryValueKind.DWord);
                            Logging.Instance.Log(3, "registry key {0} exists", regpath);
                        }
                        regPlugIn.Flush();
                        regPlugIn.Close();
                    }
                }
                catch (Exception ex)
                {

                    Logging.Instance.Log(1, "Exception thrown in RegisterLanguageProvider: {0} ", ex.Message);
                }
            }
            public static bool UnregisterLanguageProvider(ILanguageProviderMetaData ilmd)
            {
                RegistryKey regPlugIns = null;
                try
                {

                    regPlugIns = Registry.CurrentUser.OpenSubKey(Utils.PlugInsHive, RegistryKeyPermissionCheck.ReadWriteSubTree);
                    if (regPlugIns != null)
                    {
                        if (regPlugIns.GetSubKeyNames().Contains(ilmd.ProviderName, StringComparer.InvariantCultureIgnoreCase))
                            regPlugIns.DeleteSubKeyTree(ilmd.ProviderName + "\\VS" + Utils.VSYear);

                    }
                    //RegistrationServices rs = new RegistrationServices();
                    //if (ilmd.LicenseType == PatternLicense.Free)
                    //    rs.UnregisterAssembly(ilp.GetType().Assembly);
                }
                catch (Exception ex)
                {

                    Logging.Instance.Log(1, "Exception thrown in UnregisterLanguageProvider: {0} ", ex.Message);
                    return false;
                }
                finally
                {
                    if (regPlugIns != null)
                        regPlugIns.Close();
                }
                return true;
            }
            public bool Purge()
            {
                try
                {
                    string regpath = Utils.PlugInsHive + "\\" + this.CodeLangProviderName + "\\VS" + Utils.VSYear;
                    RegistryKey regPlugIn = Registry.CurrentUser.OpenSubKey(regpath, RegistryKeyPermissionCheck.ReadWriteSubTree);
                    if (regPlugIn != null)
                    {
                        int cnt = regPlugIn.GetValueNames().Count(s => s.StartsWith("location", StringComparison.InvariantCultureIgnoreCase));
                        regPlugIn.SetValue("Location" + (++cnt).ToString(CultureInfo.InvariantCulture), this.Location);
                        regPlugIn.DeleteValue("Location", false);
                        regPlugIn.Close();
                        Logging.Instance.Log(3, "purged registry value for {0} ", CodeLangProviderName);
                        return true;
                    }
                    else
                    {
                        Logging.Instance.Log(1, "Registry key {0} does NOT exist", regpath);
                    }
                }
                catch (Exception ex)
                {

                    Logging.Instance.Log(1, "Exception thrown in GetLanguageProvider: {0} ", ex.Message);
                }

                return false;
            }
            public static ILangProviderRegistration GetLanguageProviderRegistration(string providerName)
            {
                LangProviderRegistration reg = null;
                try
                {
                    string regpath = Utils.PlugInsHive + "\\" + providerName + "\\VS" + Utils.VSYear;
                    RegistryKey regPlugIn = Registry.CurrentUser.OpenSubKey(regpath, RegistryKeyPermissionCheck.ReadSubTree);
                    if (regPlugIn != null)
                    {
                        reg = new LangProviderRegistration();
                        reg.CodeLangProviderName = providerName;
                        reg._inUse = Convert.ToBoolean(regPlugIn.GetValue("InUse", 0, RegistryValueOptions.None));
                        reg.Location = regPlugIn.GetValue("Location", null, RegistryValueOptions.None) as string;
                        regPlugIn.Close();
                        Logging.Instance.Log(3, "got registry plug in for {0} ", providerName);
                    }
                    else
                    {
                        Logging.Instance.Log(1, " registry key {0} does NOT exist", regpath);
                    }
                }
                catch (Exception ex)
                {

                    Logging.Instance.Log(1, "Exception thrown in GetLanguageProvider: {0} ", ex.Message);
                }
                return reg;
            }

            public static string[] GetRegisteredLPNames()
            {
                RegistryKey regPlugIns = null;
                try
                {
                    //#if VS2015
                    //                    regPlugIns = Registry.CurrentUser.OpenSubKey(Utils.PlugInsHive, RegistryKeyPermissionCheck.ReadWriteSubTree);
                    //#else
                    regPlugIns = Registry.CurrentUser.OpenSubKey(Utils.PlugInsHive, RegistryKeyPermissionCheck.ReadSubTree);
                    //#endif
                    {

                        if (regPlugIns == null)
                        {
                            Logging.Instance.Log(1, "No registry plug in for {0} ", Utils.PlugInsHive);
                        }
                        else
                        {
                            Logging.Instance.Log(3, "registry key {0} exists", Utils.PlugInsHive);
                            const string VSVer = "VS" + Utils.VSYear;
                            string[] keyNames = regPlugIns.GetSubKeyNames();
                            List<string> realproviders = new List<string>(keyNames.Length);
                            foreach (string keyName in keyNames)
                            {

#if VS2015
                                using (RegistryKey regPlugInsVer = regPlugIns.OpenSubKey(keyName, RegistryKeyPermissionCheck.ReadWriteSubTree))
#else
                             using (RegistryKey regPlugInsVer = regPlugIns.OpenSubKey(keyName, RegistryKeyPermissionCheck.ReadSubTree))
#endif
                                {


                                    if (regPlugInsVer.GetSubKeyNames().Contains(VSVer))
                                    {
                                        using (RegistryKey regYear = regPlugInsVer.OpenSubKey(VSVer))
                                        {
                                            string location = regYear.GetValue("Location", null, RegistryValueOptions.None) as string;
#if VS2015
                                            regYear.Close();
#endif
                                            if (string.IsNullOrEmpty(location))
                                                Logging.Instance.Log(2, @"No 'Location' value under key {0}\{1}\{2}", Utils.PlugInsHive, keyName, VSVer);
                                            else
                                            {
                                                bool bAddLocation = File.Exists(location);

#if VS2015
                                                if (bAddLocation)
                                                {
                                                    // string file = Path.GetFileName(location).ToLowerInvariant();
                                                    //bAddLocation = file != "common.dll" && file != "premiumprovider.dll";
                                                    //if (bAddLocation)
                                                    //  {
                                                    //Assembly ca = Assembly.GetCallingAssembly();
                                                    string crtDir = Path.GetDirectoryName(MEFController.Instance.DPEntryAssembly.Location);
                                                    string locdir = Path.GetDirectoryName(location);
                                                    bAddLocation = string.Compare(locdir, crtDir, true) == 0;
                                                    // }

                                                }
#endif

                                                if (bAddLocation)
                                                    realproviders.Add(keyName);
                                                else
                                                {
                                                    //remove hives pointing to older locations
#if VS2015
                                                    Logging.Instance.Log(2, @"The File Location value {3} under key {0}\{1}\{2} is invalid, removing it", Utils.PlugInsHive, keyName, VSVer, location);
                                                    regPlugInsVer.DeleteSubKeyTree(VSVer, false);

#else
                                                    Logging.Instance.Log(2, @"The File Location value {3} under key {0}\{1}\{2} is invalid", Utils.PlugInsHive, keyName, VSVer, location);
#endif

                                                }
                                            }
                                        }
                                    }

                                }
                            }
                            if (realproviders.Count == 0)
                                return null;
                            return realproviders.ToArray();


                        }


                    }
                }
                catch (Exception ex)
                {

                    Logging.Instance.Log(1, "Exception thrown in GetLanguageProvider: {0} ", ex.Message);

                }
                finally
                {
                    if (regPlugIns != null)
                        regPlugIns.Close();
                }
                return null;
            }
            public override string ToString()
            {
                return string.Format("{0}|{1}|{2}", CodeLangProviderName, Location, _inUse.ToString());
            }
            #region IRegistration Members

            public string Location
            { get; set; }

            public string CodeLangProviderName
            { get; set; }
            bool _inUse;
            public bool InUse
            {
                get { return _inUse; }
                set
                {
                    UpdateLPRegistration(this.CodeLangProviderName, value);
                    _inUse = value;
                }
            }


            #endregion
        }

        /*
                // see http://stackoverflow.com/questions/725341/how-to-determine-if-a-file-matches-a-file-mask
                public static bool StrictMatchPattern(string expression, string name)
                {
                    //const char ANSI_DOS_QM = '<';
                    //const char ANSI_DOS_STAR = '>';
                    //const char DOS_DOT = '"';
                    //const int MATCHES_ARRAY_SIZE = 16;
                    expression = expression.ToLowerInvariant();
                    name = name.ToLowerInvariant();
                    int num9;
                    char ch = '\0';
                    char ch2 = '\0';
                    int[] sourceArray = new int[16];
                    int[] numArray2 = new int[16];
                    bool flag = false;
                    if (((name == null) || (name.Length == 0)) || ((expression == null) || (expression.Length == 0)))
                    {
                        return false;
                    }
                    if (expression.Equals("*") || expression.Equals("*.*"))
                    {
                        return true;
                    }
                    if ((expression[0] == '*') && (expression.IndexOf('*', 1) == -1))
                    {
                        int length = expression.Length - 1;
                        if ((name.Length >= length) && (string.Compare(expression, 1, name, name.Length - length, length, StringComparison.OrdinalIgnoreCase) == 0))
                        {
                            return true;
                        }
                    }
                    sourceArray[0] = 0;
                    int num7 = 1;
                    int num = 0;
                    int num8 = expression.Length * 2;
                    while (!flag)
                    {
                        int num3;
                        if (num < name.Length)
                        {
                            ch = name[num];
                            num3 = 1;
                            num++;
                        }
                        else
                        {
                            flag = true;
                            if (sourceArray[num7 - 1] == num8)
                            {
                                break;
                            }
                        }
                        int index = 0;
                        int num5 = 0;
                        int num6 = 0;
                        while (index < num7)
                        {
                            int num2 = (sourceArray[index++] + 1) / 2;
                            num3 = 0;
                        Label_00F2:
                            if (num2 != expression.Length)
                            {
                                num2 += num3;
                                num9 = num2 * 2;
                                if (num2 == expression.Length)
                                {
                                    numArray2[num5++] = num8;
                                }
                                else
                                {
                                    ch2 = expression[num2];
                                    num3 = 1;
                                    if (num5 >= 14)
                                    {
                                        int num11 = numArray2.Length * 2;
                                        int[] destinationArray = new int[num11];
                                        Array.Copy(numArray2, destinationArray, numArray2.Length);
                                        numArray2 = destinationArray;
                                        destinationArray = new int[num11];
                                        Array.Copy(sourceArray, destinationArray, sourceArray.Length);
                                        sourceArray = destinationArray;
                                    }
                                    if (ch2 == '*')
                                    {
                                        numArray2[num5++] = num9;
                                        numArray2[num5++] = num9 + 1;
                                        goto Label_00F2;
                                    }
                                    if (ch2 == '>')
                                    {
                                        bool flag2 = false;
                                        if (!flag && (ch == '.'))
                                        {
                                            int num13 = name.Length;
                                            for (int i = num; i < num13; i++)
                                            {
                                                char ch3 = name[i];
                                                num3 = 1;
                                                if (ch3 == '.')
                                                {
                                                    flag2 = true;
                                                    break;
                                                }
                                            }
                                        }
                                        if ((flag || (ch != '.')) || flag2)
                                        {
                                            numArray2[num5++] = num9;
                                            numArray2[num5++] = num9 + 1;
                                        }
                                        else
                                        {
                                            numArray2[num5++] = num9 + 1;
                                        }
                                        goto Label_00F2;
                                    }
                                    num9 += num3 * 2;
                                    switch (ch2)
                                    {
                                        case '<':
                                            if (flag || (ch == '.'))
                                            {
                                                goto Label_00F2;
                                            }
                                            numArray2[num5++] = num9;
                                            goto Label_028D;

                                        case '"':
                                            if (flag)
                                            {
                                                goto Label_00F2;
                                            }
                                            if (ch == '.')
                                            {
                                                numArray2[num5++] = num9;
                                                goto Label_028D;
                                            }
                                            break;
                                    }
                                    if (!flag)
                                    {
                                        if (ch2 == '?')
                                        {
                                            numArray2[num5++] = num9;
                                        }
                                        else if (ch2 == ch)
                                        {
                                            numArray2[num5++] = num9;
                                        }
                                    }
                                }
                            }
                        Label_028D:
                            if ((index < num7) && (num6 < num5))
                            {
                                while (num6 < num5)
                                {
                                    int num14 = sourceArray.Length;
                                    while ((index < num14) && (sourceArray[index] < numArray2[num6]))
                                    {
                                        index++;
                                    }
                                    num6++;
                                }
                            }
                        }
                        if (num5 == 0)
                        {
                            return false;
                        }
                        int[] numArray4 = sourceArray;
                        sourceArray = numArray2;
                        numArray2 = numArray4;
                        num7 = num5;
                    }
                    num9 = sourceArray[num7 - 1];
                    return (num9 == num8);
                }
        */
        public const string PlugInsHive = @"Software\DesignPatterns\Plugins";
        public const WindowPopupEnum AllPopUpWnds = WindowPopupEnum.CodeWindow | WindowPopupEnum.ErrorList | WindowPopupEnum.ReferenceItem | WindowPopupEnum.XAMLEditor | WindowPopupEnum.HTML;
#if VS2008
        public const string VSVersion = "9.0";
        public const string VSYear = "2008";
#endif
#if VS2010
            public const string VSVersion = "10.0";
            public const string VSYear = "2010";
#endif
#if VS2012
            public const string VSVersion = "11.0";
            public const string VSYear = "2012";
#endif
#if VS2013
        public const string VSVersion = "12.0";
        public const string VSYear = "2013";
#endif
#if VS2015
        public const string VSVersion = "14.0";
        public const string VSYear = "2015";
#endif

        static public T GetAsService<T>(this IServiceProvider sp) where T : class
        {
            return sp.GetService(typeof(T)) as T;
        }
        static public string Replace(this string str, string oldValue, string newValue, StringComparison comparison)
        {
            StringBuilder sb = new StringBuilder(str.Length);

            try
            {
                int previousIndex = 0;
                int index = str.IndexOf(oldValue, comparison);
                while (index != -1)
                {
                    sb.Append(str.Substring(previousIndex, index - previousIndex));
                    sb.Append(newValue);
                    index += oldValue.Length;

                    previousIndex = index;
                    index = str.IndexOf(oldValue, index, comparison);
                }
                sb.Append(str.Substring(previousIndex));

                return sb.ToString();
            }
            finally
            {
                sb.Length = 0;
            }
        }

        static public string ExecuteArgSubstitution(ITemplateFile itf, IEnumerable<ITemplateArgument> args)
        {
            try
            {
                string result = itf.FileTemplateContent;
                foreach (ITemplateArgument arg in args)
                {
                    string target = string.Format("${0}$", arg.ArgumentName);
                    result = result.Replace(target, arg.Value, StringComparison.InvariantCultureIgnoreCase);
                }
                return result;
            }
            catch
            {

                throw;
            }
        }


        //see http://stackoverflow.com/questions/2454456/cancel-installation-from-my-custom-action
        /*
               class WindowWrapper : IWin32Window
               {
                   [DllImport("user32.dll")]
                   [return: MarshalAs(UnmanagedType.Bool)]
                   static extern internal bool IsWindowVisible(IntPtr hWnd);
                   internal WindowWrapper(IntPtr handle)
                   {
                       Handle = handle;
                   }

                   public IntPtr Handle { get; private set; }
               }
       
               static public IWin32Window GetMainWindow(this System.Diagnostics.Process pr)
               {
                   if (pr == null || pr.MainWindowHandle == IntPtr.Zero || !WindowWrapper.IsWindowVisible(pr.MainWindowHandle))
                       return null;
                   return new WindowWrapper(pr.MainWindowHandle);
               }
                */
        static public DialogResult ShowModalDlg(string message, string title, MessageBoxButtons buttons, MessageBoxIcon icon)
        {//for the msi exec the window is with caption 'DesignPatterns for VS 2010' and class MsiDialogCloseClass, or 'Windows Installer' and '#32770 (Dialog)'
            // Create a host form that is a TopMost window which will be the 
            // parent of the MessageBox.


            NativeWindow nativeWnd = new NativeWindow();
            try
            {
                IntPtr hWnd = (from p in Process.GetProcessesByName("msiexec")
                               where (p.MainWindowHandle != IntPtr.Zero && (p.MainWindowTitle == "DesignPatterns for VS " + Utils.VSYear || p.MainWindowTitle == "Windows Installer"))
                               select p.MainWindowHandle).SingleOrDefault();
                //nativeWnd = NativeWindow.FromHandle(hWnd); //does not work

                //IntPtr hwnd = Process.GetCurrentProcess().MainWindowHandle; is always 0
                if (hWnd == IntPtr.Zero)
                    return MessageBox.Show(message, title, buttons, icon, MessageBoxDefaultButton.Button1, MessageBoxOptions.DefaultDesktopOnly);
                else
                {
                    nativeWnd.AssignHandle(hWnd);
                    return MessageBox.Show(nativeWnd, message, title, buttons, icon);
                }

            }
            catch (Exception ex)
            {
                return MessageBox.Show(ex.Message, "Exception thrown in ShowModalDlg", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, MessageBoxOptions.DefaultDesktopOnly);
            }
            finally { nativeWnd.ReleaseHandle(); }

        }

#if !VS2015
        public static void UnzipLibrary(string zipPath, string destFolder)
        {
            try
            {

                // Specifying Console.Out here causes diagnostic msgs to be sent to the Console
                // In a WinForms or WPF or Web app, you could specify nothing, or an alternate
                // TextWriter to capture diagnostic messages.

                var options = new ReadOptions { StatusMessageWriter = System.Console.Out };
                using (ZipFile zip = ZipFile.Read(zipPath, options))
                {
                    // This call to ExtractAll() assumes:
                    //   - none of the entries are password-protected.
                    //   - want to extract all entries to current working directory
                    //   - none of the files in the zip already exist in the directory;
                    //     if they do, the method will throw.
                    zip.ExtractAll(destFolder);
                }
            }
            catch (Exception ex)
            {
                Logging.Instance.Log(1, "Install: exception thrown wile unziping {0} into {1}: {2}", zipPath, destFolder, ex.Message);
                throw;
            }
        }
#endif

        static public void RemoveReadOnlyAttribute(this System.IO.DirectoryInfo dir, System.IO.SearchOption so)
        {
            try
            {
                // Take a snapshot of the file system.
                // This method assumes that the application has discovery permissions 
                // for all folders under the specified prex.
                IEnumerable<System.IO.FileInfo> fileList = dir.GetFiles("*.*", so);//System.IO.SearchOption.AllDirectories);

                foreach (FileInfo f in fileList)
                {
                    if ((uint)(f.Attributes & FileAttributes.ReadOnly) != 0)
                    {
                        f.Attributes ^= FileAttributes.ReadOnly;// remove read only
                    }
                }

            }
            catch //(Exception ex)
            {

                throw;
            }

        }

    }
    public enum ResultEnum
    {
        Success = 0, UnknownError = 1, ItemExists = 2, FileExists = 3, AutomationError = 4, MissingFileTemplate = 5, BadFileExtension = 6,
        UnlicensedLanguageProvider = 7, UserCancelled = 8
    }
    public class EventValidaton : EventArgs
    {
        public int SelIndex;
        public IEnumerable<String> Files;
        public string ErrorMsg;
        public ResultEnum Result;
        public ILanguageProvider LangProvider;
    }

    public class FileData
    {
        public string itemName;
        public string fileNameCopy;
    }
    public class EventGeneration : EventArgs
    {
        public FileData[] Files;
        public string ErrorMsg;
        public string Url;
        public ResultEnum Result;
        public ILanguageProvider LangProvider;
    }
    public delegate void CanUpdateVSProjectDelegate(IServiceProvider sender, EventValidaton e);
    public delegate void UpdateVSProjectDelegate(IServiceProvider sender, EventGeneration e);

    [Flags]
    public enum WindowPopupEnum : uint
    {
        None = 0,
        [Description("Code Window")]//code and xml
        CodeWindow = 1,
        [Description("XAML Editor")]
        XAMLEditor = 2,
        [Description("Reference Item")]
        ReferenceItem = 4,
        [Description("Error List")]
        ErrorList = 8,
        [Description("HTML Context")]
        HTML = 16,

    }
    [Flags]
    public enum CodeLangEnum : ushort
    {
        [Description("Unknown Project Type")]
        None = 0,
        [Description("VB.Net")]
        VB = 1,
        [Description("c#")]
        CSharp = 2,
        [Description("C/C++")]
        NativeCpp = 4,
        [Description("C++/CLI")]
        ManagedCpp = 8,
        [Description("Type Script")]
        TypeScript = 16,
        [Description("F#")]
        FSharp = 32
    }
    [Flags]
    public enum PatternLicense : uint
    {
        Free = 0,
        Trial = 1,
        Premium = 2,
    }

    public enum StorageType : uint
    {
        Text = 0,
        FileSystem = 1,
        Zip = 2,
        EncryptedZip = 3,
        EmbeddedResource = 5,
        EmbeddedEncryptedResource = 6,
        Url = 7,
    }


    public interface IProviderMetaData
    {
        [DefaultValue("")]
        string Description { get; }
        [DefaultValue("")]
        string ProviderName { get; }
        [DefaultValue(PatternLicense.Free)]
        PatternLicense LicenseType { get; }
    }

    public interface ISearchProviderMetaData : IProviderMetaData
    {//see below for custom attribute
        [DefaultValue(WindowPopupEnum.None)]
        WindowPopupEnum SupportedWnds { get; }
        [DefaultValue(null)]
        string URL { get; }
    }
    public interface ILanguageProviderMetaData : IProviderMetaData
    {//see below for custom attribute
        [DefaultValue(CodeLangEnum.None)]
        CodeLangEnum SupportedLangs { get; }
        [DefaultValue(CodeLangEnum.None)]
        CodeLangEnum CompatibleLangs { get; }
        [DefaultValue(null)]
        string LPIndexFile { get; }
        [DefaultValue("TemplateLibrary")]
        string LPRepositoryName { get; }
        [DefaultValue(null)]
        string[] Extensions { get; }
        [DefaultValue(StorageType.FileSystem)]
        StorageType SavedAs { get; }
    }
    public interface ILangProviderRegistration
    {
        string Location { get; }
        string CodeLangProviderName { get; }
        bool InUse { get; set; }
        bool Purge();
    }

    public interface IProjectInfo : IServiceProvider
    {
        string ProjectName { get; }
        string FileName { get; }
        string SolutionFileName { get; }
        CodeLangEnum CodeLang { get; }
        IEnumerable<ILanguageProvider> CreateSpecificLanguageProviders(Predicate<ILanguageProviderMetaData> filter);
        TimeSpan LoadingTime { get; }
        IWizardWindow WizardWindow { get; }
        // IWin32Window VSWindow{ get; }
        Dictionary<string, string> ProjectProperties { get; }
        event CanUpdateVSProjectDelegate CanUpdateVSProject;
        event UpdateVSProjectDelegate UpdateVSProject;
        void OnValidatePattern(object sender, EventValidaton e);
        void OnUpdateVSProject(object sender, EventGeneration e);
    }
    public sealed class MEFController
    {
        public static IProjectInfo ProjectInfo { get; set; }

        public static bool IsLanguageSupported(LanguageProviderEx lpex, CodeLangEnum lang)
        {
            return (lpex.Metadata.SupportedLangs & lang) != default(CodeLangEnum);
        }

        public IEnumerable<LanguageProviderEx> this[CodeLangEnum lang]
        {

            get
            {
                try
                {//LangProviders get instantiated below
                    return MEFController.Instance.LoadedLangProviders.Where(x => IsLanguageSupported(x, lang));
                }
                catch (Exception ex)
                {
                    Logging.Instance.Log(1, "Exception thrown in MEFController.this[CodeLangEnum lang] : {0}", ex.Message);
                    return null;
                }

            }

        }

        public ILanguageProviderMetaData this[string lpName]
        {
            get
            {
                try
                {
                    LanguageProviderEx elemEx = MEFController.Instance.LoadedLangProviders.SingleOrDefault(x => string.Compare(x.Metadata.ProviderName, lpName, true) == 0);
                    if (elemEx == null)
                        return null;
                    return elemEx.Metadata;
                }
                catch (Exception ex)
                {
                    Logging.Instance.Log(1, "Exeption thrown in MEFController.this[string searchProvider] : {0}", ex.Message);
                    return null;
                }
            }
        }

        public ISearchProviderMetaData GetProviderSearchMetadata(string searchProvider)
        {

            try
            {
                SearchProviderEx elemEx = MEFController.Instance.LoadedSearchProviders.SingleOrDefault(x => string.Compare(x.Metadata.ProviderName, searchProvider, true) == 0);
                if (elemEx == null)
                    return null;
                return elemEx.Metadata;
            }
            catch (Exception ex)
            {
                Logging.Instance.Log(1, "Exeption thrown in MEFController.GetProviderSearchMetadata(string searchProvider) : {0}", ex.Message);
                return null;
            }

        }
        /*public IEnumerable<ISearchProviderMetaData> SearchMetadata 
        {
            get
            {
                try
                {
                    return MEFController.Instance.LoadedSearchProviders.Select(x => x.Metadata);
                }
                catch (Exception ex)
                {
                    Logging.Instance.Log(1, "Exeption thrown in MEFController.GetSearchMetadata : {0}", ex.Message);
                    return null;
                }
            }

        }*/
        public IEnumerable<SearchProvider> SearchProviders
        {
            get
            {
                try
                {
                    return MEFController.Instance.LoadedSearchProviders.Select(x => x.Value);
                }
                catch (Exception ex)
                {
                    Logging.Instance.Log(1, "Exeption thrown in MEFController.GetSearchProvider : {0}", ex.Message);
                    return null;
                }
            }

        }

        #region Comparers
        internal class FileComparer : IEqualityComparer<string>
        {
            public bool Equals(string path1, string path2)
            {
                //Check whether the compared objects reference the same data. 
                if (Object.ReferenceEquals(path1, path2)) return true;

                //Check whether any of the compared objects is null. 
                if (Object.ReferenceEquals(path1, null) || Object.ReferenceEquals(path2, null))
                    return false;

                path1 = System.IO.Path.GetFullPath(Environment.ExpandEnvironmentVariables(path1));
                path2 = System.IO.Path.GetFullPath(Environment.ExpandEnvironmentVariables(path2));
                return string.Compare(path1, path2, true) == 0;
            }

            public int GetHashCode(string path)
            {
                return path == null ? 0 : System.IO.Path.GetFullPath(Environment.ExpandEnvironmentVariables(path)).ToUpperInvariant().GetHashCode();
            }
        }

        internal class SearchProviderComparer : IEqualityComparer<SearchProviderEx>
        {
            public bool Equals(SearchProviderEx prex1, SearchProviderEx prex2)
            {
                //Check whether the compared objects reference the same data. 
                if (Object.ReferenceEquals(prex1, prex2)) return true;

                //Check whether any of the compared objects is null. 
                if (Object.ReferenceEquals(prex1, null) || Object.ReferenceEquals(prex2, null))
                    return false;

                string p1 = prex1.Metadata.ProviderName;
                string p2 = prex2.Metadata.ProviderName;
                return string.Compare(p1, p2, true) == 0;
            }

            public int GetHashCode(SearchProviderEx prex)
            {
                return prex == null ? 0 : prex.Metadata.ProviderName.ToUpperInvariant().GetHashCode();
            }
        }

        internal class LanguageProviderComparer : IEqualityComparer<LanguageProviderEx>
        {
            public bool Equals(LanguageProviderEx prex1, LanguageProviderEx prex2)
            {
                //Check whether the compared objects reference the same data. 
                if (Object.ReferenceEquals(prex1, prex2)) return true;

                //Check whether any of the compared objects is null. 
                if (Object.ReferenceEquals(prex1, null) || Object.ReferenceEquals(prex2, null))
                    return false;

                string p1 = prex1.Metadata.ProviderName;
                string p2 = prex2.Metadata.ProviderName;
                return string.Compare(p1, p2, true) == 0;
            }

            public int GetHashCode(LanguageProviderEx prex)
            {
                return prex == null ? 0 : prex.Metadata.ProviderName.ToUpperInvariant().GetHashCode();
            }
        }


        #endregion
        public IDictionary<ILanguageProviderMetaData, ILanguageProvider> GetLPInstatntiated(Predicate<ILanguageProviderMetaData> filter)
        {
            if (filter == null)
                return LoadedLangProviders.ToDictionary(pex => pex.Metadata, pv => pv.Value as ILanguageProvider);
            else
                return LoadedLangProviders.Where(p => filter(p.Metadata)).ToDictionary(pex => pex.Metadata, pv => pv.Value);
        }

        static IWin32Window ParentWnd { get { return (ProjectInfo == null) ? null : (ProjectInfo.GetAsService<IWizardWindow>() as IWin32Window); } }
        #region MEF
#if VS2015
        //composition of 1 elem fails for VS2015
        [ImportMany(typeof(IAboutWnd))]
        IEnumerable<IAboutWnd> AboutWnds { get; set; }
        public IAboutWnd AboutWnd
        {
            get
            {
                return AboutWnds.FirstOrDefault();
            }
        }
        [ImportMany("HandleTrialActivation")]
        IEnumerable<Lazy<Action<IWizardWindow>>> _HandleTrialActivations { get; set; }
        public Action<IWizardWindow> HandleTrialActivation
        {
            get
            {
                var lh = _HandleTrialActivations.FirstOrDefault();

                return (lh == null) ? null : lh.Value;
            }
        }
#else

        // [Import(typeof(IAboutWnd),AllowDefault = true, AllowRecomposition = true)]
        [Import(AllowDefault = true)]
        Lazy<IAboutWnd> _aboutWnd { get; set; }
        public IAboutWnd AboutWnd
        {
            get
            {
                return (_aboutWnd == null) ? null : _aboutWnd.Value;
            }
        }
        [Import("HandleTrialActivation", AllowDefault = true)]
        Lazy<Action<IWizardWindow>> _HandleTrialActivation { get; set; }
        public Action<IWizardWindow> HandleTrialActivation
        {
            get
            {
                return (_HandleTrialActivation == null) ? null : _HandleTrialActivation.Value;
            }
        }
#endif





#pragma warning disable 0649
        [ImportMany(typeof(ILanguageProvider), AllowRecomposition = true)]
        private IEnumerable<LanguageProviderEx> _loadedLangProviders;// { get; set; }

        [ImportMany(typeof(SearchProvider), AllowRecomposition = true)]
        private IEnumerable<SearchProviderEx> _loadedSearchProviders;// { get; set; }
#pragma warning restore 0649

        static readonly IEqualityComparer<LanguageProviderEx> _langProviderExComparer = new LanguageProviderComparer();
        //filter non language providers, may be other dedicated attribute
        public IEnumerable<LanguageProviderEx> LoadedLangProviders
        {
            get
            {//eliminate duplicates created by MEF && checks for invalid lang providers
                return _loadedLangProviders.Distinct(_langProviderExComparer).Where(x => !string.IsNullOrEmpty(x.Metadata.LPIndexFile));
            }
        }
        public IEnumerable<ILanguageProviderMetaData> LoadedLangProvidersMetada
        {
            get
            {//eliminate duplicates created by MEF && checks for invalid lang providers
                return LoadedLangProviders.Select(pex => pex.Metadata);
            }
        }
        static readonly IEqualityComparer<SearchProviderEx> _searchProviderExComparer = new SearchProviderComparer();
        //filter non search providers, may be other dedicated attribute
        public IEnumerable<SearchProviderEx> LoadedSearchProviders
        {
            get
            {
                //eliminate duplicates created by MEF & checks for invalid search providers
                return _loadedSearchProviders.Distinct(_searchProviderExComparer).Where(x => !string.IsNullOrEmpty(x.Metadata.URL));
            }
        }

        private CompositionContainer _container;
        void InitMEF()
        {
            try
            {

                //An aggregate catalog that combines multiple catalogs
                var catalog = new AggregateCatalog();
                LoadCatalog(catalog);
                //Create the CompositionContainer with the parts in the catalog
                _container = new CompositionContainer(catalog);


                //Fill the imports of this object
                _container.ComposeParts(this);
                if (ProjectInfo == null)
                    return; //is installing
                //check for duplicate language provider names
                ///////////////////////////
                try
                {
                    string[] registeredNames = Utils.LangProviderRegistration.GetRegisteredLPNames();
                    if (registeredNames == null)
                    {
                        /*
                        Form frm = _parentWnd as Form;
                        if (frm.InvokeRequired)
                        {
                            DialogResult res = (DialogResult)frm.Invoke((Action)(() => MessageBox.Show(frm, "No registered language providers in InitMEF", "MEFController")));
                        }
                        else
                            MessageBox.Show(_parentWnd, "No registered language providers in InitMEF", "MEFController");
                        */
                        return;
                    }
                    string[] allLoadedProviders = MEFController.Instance.LoadedLangProviders.Select(px => px.Metadata.ProviderName).ToArray();

                    foreach (string lp in registeredNames.Except(allLoadedProviders, StringComparer.InvariantCultureIgnoreCase))//allLoadedProviders)
                    {
                        //will load but not show up in the config form
                        ParentWnd.ShowMessageBox("The registered provider '" + lp + "' has not loaded.", "MEFController");
                        Logging.Instance.Log(2, "The registered provider '" + lp + "' has not loaded.");

                    }

                    foreach (string rp in allLoadedProviders.Except(registeredNames, StringComparer.InvariantCultureIgnoreCase))//allLoadedProviders)
                    {
                        //will load but not show up in the config form
                        // DialogResult res = ParentWnd.ShowMessageBox("The loaded provider '" + rp + "' is missing the registration.", "MEFController");

                    }
                    ILookup<string, LanguageProviderEx> groupsByLPName = MEFController.Instance.LoadedLangProviders.ToLookup(p => p.Metadata.ProviderName, StringComparer.InvariantCultureIgnoreCase);
                    //IGrouping<string, LanguageProviderEx> duplicateProvider = groupsByLPName.FirstOrDefault(g => g.Count() > 1);
                    //if (duplicateProvider != null)
                    if (groupsByLPName.Any(g => g.Count() > 1))
                    {

                        // ILangProviderRegistration[] allRegistrations = registeredNames.Select(s => Utils.LangProviderRegistration.GetLanguageProviderRegistration(s)).ToArray();

                        var badRegistrations = registeredNames.Except(allLoadedProviders, StringComparer.InvariantCultureIgnoreCase);
                        /*
                        IEnumerable<LanguageProviderEx> providersEx = groupsByLPName[duplicateProvider.Key];
                        var providersEx = duplicateProvider[duplicateProvider.Key];
                        var regsByLocation = allRegistrations.Join(providersEx, r => r.Location, p => p.Value.GetType().Assembly.Location, (reg, prov) =>
                            new { reg, prov.Metadata }).ToArray();
                        IEnumerable<ILangProviderRegistration> invalidRegistrations = regsByLocation.Where(a => a.Metadata.CodeLangProviderName != a.reg.CodeLangProviderName).Select(a => a.reg).Distinct();
                        ILangProviderRegistration invalidRegistration = invalidRegistrations.FirstOrDefault();
                        */
                        ILangProviderRegistration invalidRegistration = badRegistrations.Select(s => Utils.LangProviderRegistration.GetLanguageProviderRegistration(s)).FirstOrDefault();
                        if (invalidRegistration != null)
                        {
                            ParentWnd.ShowMessageBox("Invalid Registration for '" + invalidRegistration.CodeLangProviderName + "'\r\nRemoving it from registry.", "MEFController");
                            invalidRegistration.Purge();
                            ReComposeParts();

                        }
                        return;
                    }
                }
                catch (InvalidOperationException ex)
                {

                    ParentWnd.ShowMessageBox("Duplicate providers in InitMEF " + ex.Message, "MEFController");

                }
                catch (Exception ex)
                {

                    ParentWnd.ShowMessageBox("Exception thrown  in InitMEF:" + ex.Message, "MEFController");

                }



            }

            catch (CompositionException compositionException)
            {
                DialogResult res = ParentWnd.ShowMessageBox("CompositionException thrown in  InitMEF:" + compositionException.Message, "MEFController");

            }
            catch (ReflectionTypeLoadException ex)
            {
                Logging.Instance.Log(1, "ReflectionTypeLoadException thrown in  InitMEF:{0}", ex.Message);
            }
            catch (Exception ex)
            {
                Logging.Instance.Log(1, "Exception thrown in  InitMEF:{0}", ex.Message);
            }
        }

        internal void ReComposeParts()//AggregateCatalog catalog)
        {
            AggregateCatalog catalog = _container.Catalog as AggregateCatalog;
            int partCount = 0, catalogCount = 0;
            foreach (var c in catalog.Catalogs.ToArray())
            {
                //foreach (var p in c.Parts){}
                catalogCount++;
                partCount += c.Parts.Count();
                catalog.Catalogs.Remove(c);
            }
            Logging.Instance.Log(3, "Before purge part no = {0}, catalog count = {1}", partCount, catalogCount);
            //clear the parts
            _container.ComposeParts(this);
            //reload again
            LoadCatalog(catalog);
            //Fill the imports of this object
            _container.ComposeParts(this);
            partCount = catalogCount = 0;
            foreach (var c in catalog.Catalogs.ToArray())
            {
                catalogCount++;
                partCount += c.Parts.Count();

            }
            _container.ComposeParts(this);
            Logging.Instance.Log(3, "After purge part no = {0}, catalog count = {1}", partCount, catalogCount);
        }

        private static void LoadCatalog(AggregateCatalog catalog)
        {
            Assembly assm = MEFController.Instance.DPEntryAssembly;
            string[] regProviders = Utils.LangProviderRegistration.GetRegisteredLPNames();

            if (regProviders == null || !regProviders.Any())
            {//no registration in registry
                //catalog.Catalogs.Add(new AssemblyCatalog(assm));//gets only the default providers
                catalog.Catalogs.Add(new DirectoryCatalog(Path.GetDirectoryName(assm.Location)));
            }
            else
            {//some registration in registry
                IEqualityComparer<string> fc = new FileComparer();
                string[] locations = Utils.LangProviderRegistration.GetRegisteredLPNames().Select(p =>
                {
                    ILangProviderRegistration ireg = Utils.LangProviderRegistration.GetLanguageProviderRegistration(p);
                    if (ireg == null || string.IsNullOrEmpty(ireg.Location))
                        return null;//filter bogus registrations
                    return ireg;
                }).Where(i => i != null).Select(r => r.Location).Distinct(fc).Where(f => File.Exists(f)).ToArray();

                if (locations.Length == 0)
                {
                    Logging.Instance.Log(2, "No assemblies were registered as plugins");
                    catalog.Catalogs.Add(new DirectoryCatalog(Path.GetDirectoryName(assm.Location)));
                    //catalog.Catalogs.Add(new AssemblyCatalog(assm));
                }
                else
                {
                    Logging.Instance.Log(3, locations.Length + "assemblies were registered as plugins");
                    foreach (string path in locations)
                        catalog.Catalogs.Add(new AssemblyCatalog(path));
                    if (MEFController.ParentWnd == null)//installing
                    {
                        if (!locations.Contains(assm.Location, StringComparer.InvariantCultureIgnoreCase))
                            catalog.Catalogs.Add(new AssemblyCatalog(assm.Location));

                    }
                }
            }
        }
        #endregion
        private MEFController() { }

        private static volatile MEFController _instance;
        private static readonly object _syncRoot = new Object();
      //  private Assembly _callingAssembly;
        public static MEFController Instance
        {
            get
            {
                if (_instance == null)
                {
                    lock (_syncRoot)
                    {
                        if (_instance == null)
                        {
                            try
                            {
                               // Assembly ca = Assembly.GetCallingAssembly();
                                _instance = new MEFController();
                                _instance.DPEntryAssembly = Assembly.GetCallingAssembly();
                                _instance.InitMEF();
                            }
                            catch (Exception ex)
                            {

                                Logging.Instance.Log(1, "Exception in MEFController InitMef:" + ex.Message);
                            }
                        }
                    }
                }
                return _instance;
            }
        }

        public Assembly DPEntryAssembly { get; private set; }


        //#else
        //        //just for .net 4.0 and later because of LazyThreadSafetyMode.PublicationOnly
        //        private static readonly Lazy<MEFController> _instance
        //      = new Lazy<MEFController>(() =>
        //      {
        //          MEFController mc = new MEFController();
        //          mc.InitMEF();
        //          return mc;
        //      }, System.Threading.LazyThreadSafetyMode.ExecutionAndPublication);

        //        public static MEFController Instance
        //        {
        //            get
        //            {

        //                return _instance.Value;
        //            }
        //        }
        //#endif
    }

    public sealed class Logging
    {
        private static volatile Logging _instance;
        private static object _syncRoot = new Object();

        private Logging()
        {

            RegistryKey Mykey = Registry.CurrentUser.OpenSubKey(Utils.AppHive);
            if (Mykey != null)
            {
                _loglvl = (int)Mykey.GetValue("LoggingLevel", 3);
                _fileMaxSize = (int)Mykey.GetValue("LogFileSize", 1024 * 1024);
                Mykey.Close();
            }
            else
            {
                _loglvl = 3;
                _fileMaxSize = 1024 * 1024;
            }
        }

        public static Logging Instance
        {
            get
            {
                if (_instance == null)
                {
                    lock (_syncRoot)
                    {
                        if (_instance == null)
                            _instance = new Logging();
                    }
                }

                return _instance;
            }
        }
        //static Logging()
        //{

        //}
        public void Init(int lvl, long filesize, string FilePath)
        {
            _path = FilePath;
            _loglvl = lvl;
            _fileMaxSize = filesize;
            CheckAndFlip(FilePath);
        }



        public void CheckAndFlip(string FilePath)
        {
            FileInfo fitmp = new FileInfo(FilePath);
            if (fitmp.Exists)
            {
                if (fitmp.Length > _fileMaxSize)
                {
                    //delete the *.log-
                    string FilePath2 = FilePath + "-";
                    if (File.Exists(FilePath2))
                    {
                        File.Delete(FilePath2);
                    }

                    File.Move(FilePath, FilePath2);
                }
            }

            _path = FilePath;       // capture and save to the _path
        }

        public void Log(int lvl, string logtxt)
        {
            lock (this)
            {
                if (lvl > _loglvl || string.IsNullOrEmpty(_path)) return;

                using (StreamWriter w = File.AppendText(_path))
                {
                    w.WriteLine("{0:yy/MM/dd HH:mm:ss:ffffff}:{1}", DateTime.Now, logtxt);

                }
            }
        }

        public void Log(int lvl, string message, params object[] arglist)
        {
            if (lvl <= _loglvl)
                Log(lvl, string.Format(message, arglist));
        }

        private void Log(int lvl, String logMessage, TextWriter w)
        {
            w.WriteLine("{0:yy/MM/dd HH:mm:ss:ffffff}:{1}", DateTime.Now, logMessage);
            // Update the underlying file.
            w.Flush();
        }
        private string _path;
        int _loglvl;
        private long _fileMaxSize;
    }
}
